//
//  NetworkUtils.swift
//  ios-template
//
//  Created by lvyii000 on 2021/8/23.
//  Copyright © 2021 耳東陳. All rights reserved.
//

import Alamofire
import Foundation
import Moya
import SwiftyJSON

/// 网络超时时长
private var requestTimeOut: Double = 10
// 请求回调
typealias RequestCallback = ((ResponseModel) -> Void)
/// 网络错误的回调
typealias errorCallback = (() -> Void)

let dataKey = "obj"
let messageKey = "message"
let codeKey = "code"

/// 网络请求的基本设置
private let myEndpointClosure = { (target: TargetType) -> Endpoint in

    let url = target.baseURL.absoluteString + target.path
    var task = target.task
    
    var endpoint = Endpoint(
        url: url,
        sampleResponseClosure: { .networkResponse(200, target.sampleData) },
        method: target.method,
        task: task,
        httpHeaderFields: target.headers
    )
    
    return endpoint
}

/// 网络请求的设置
private let requestClosure = { (endpoint: Endpoint, done: MoyaProvider.RequestResultClosure) in
    do {
        var request = try endpoint.urlRequest()
        // 设置请求时长
        request.timeoutInterval = requestTimeOut
        request.addValue(store.token,forHTTPHeaderField: "token")
        // 打印请求参数
        if let requestData = request.httpBody {
            print("请求的url：\(request.url!)" + "\n" + "\(request.httpMethod ?? "")" + "发送参数" + "\(String(data: request.httpBody!, encoding: String.Encoding.utf8) ?? "")")
        } else {
            print("请求的url：\(request.url!)" + "\(String(describing: request.httpMethod))")
        }

        if let header = request.allHTTPHeaderFields {
            print("请求头内容\(header)")
        }

        done(.success(request))
    } catch {
        done(.failure(MoyaError.underlying(error, nil)))
    }
}

/// 监听网络请求
private let networkPlugin = NetworkActivityPlugin.init { changeType, _ in
    print("networkPlugin \(changeType)")
    // targetType 是当前请求的基本信息
    switch changeType {
    case .began:
        print("开始请求网络")

    case .ended:
        print("结束")
    }
}

/// 网络请求发送的核心初始化方法，创建网络请求对象
fileprivate let Provider = MoyaProvider<MultiTarget>(endpointClosure: myEndpointClosure, requestClosure: requestClosure, plugins: [networkPlugin], trackInflights: false)

/// 网络请求的基础方法
/// - Parameters:
///   - target: 接口
///   - showFailAlert: 是否显示网络请求失败的弹框
///   - successCallback: 成功的回调
///   - failureCallback: 失败的回调
/// - Returns: 取消当前网络请求Cancellable实例
func NetWorkRequest(_ target: TargetType, successCallback:@escaping RequestCallback, failureCallback: RequestCallback? = nil, handleError: Bool = true) -> Cancellable? {
    
    // 先判断网络是否有链接 没有的话直接返回--代码略
    if !UIDevice.isNetworkConnect {
        // code = 9999 代表无网络
        errorHandler(code: 9999, message: "网络似乎出现了问题", handleError: handleError, failure: failureCallback)
        return nil
    }
    return Provider.request(MultiTarget(target)) { result in
        switch result {
        case let .success(response):
            do {
                let jsonData = try JSON(data: response.data)
                print("返回结果是：\(jsonData)")
                if !validateRepsonse(response: jsonData.dictionary, handleError: handleError, failure: failureCallback) { return }
                let respModel = ResponseModel()
                respModel.code = jsonData[codeKey].intValue
                respModel.message = jsonData[messageKey].stringValue
                respModel.data = jsonData[dataKey]
                successCallback(respModel)

            } catch {
                errorHandler(code: 10000, message: "JSON解析失败", handleError: handleError, failure: failureCallback)
            }
        case let .failure(error as NSError):
            errorHandler(code: error.code, message: "网络连接失败", handleError: handleError, failure: failureCallback)
        }
    }
}


/// 请求数据处理
/// - Parameters:
///   - response: 后台返回的数据
///   - showFailAlet: 是否显示失败的弹框
///   - failure: 失败的回调
/// - Returns: 数据是否有效
private func validateRepsonse(response: [String: JSON]?, handleError: Bool, failure: RequestCallback?) -> Bool {

    var errorMessage: String = ""
    if response != nil {
        if !response!.keys.contains(codeKey) {
            errorMessage = "返回值不匹配：缺少状态码"
        } else if response![codeKey]!.int == 500 {
            errorMessage = response![messageKey]?.string ?? "服务器内部错误"
        }
    } else {
        errorMessage = "服务器开小差了"
    }
    if errorMessage.count > 0 {
        var code: Int = 999
        if let codeNum = response?[codeKey]?.int {
            code = codeNum
        }
        if let msg = response?[messageKey]?.stringValue {
            errorMessage = msg
        }
        errorHandler(code: code, message: errorMessage, handleError: handleError, failure: failure)
        return false
    }

    return true
}

/// 错误处理
/// - Parameters:
///   - code: code码
///   - message: 错误消息
///   - handleError: 是否显示网络请求失败的弹框
///   - failure: 网络请求失败的回调
private func errorHandler(code: Int, message: String, handleError: Bool, failure: RequestCallback?) {
    print("发生错误：\(code)--\(message)")
    let model = ResponseModel()
    model.code = code
    model.message = message
    if handleError {
        // 弹框
        print("弹出错误信息弹框\(message)")
    }
    failure?(model)
}

class ResponseModel {
    var code: Int = 999
    var message: String = ""
    // 这里的data用String类型 保存response.data
    var data: JSON = ""
}


/// 基于Alamofire,网络是否连接，，这个方法不建议放到这个类中,可以放在全局的工具类中判断网络链接情况
/// 用计算型属性是因为这样才会在获取isNetworkConnect时实时判断网络链接请求，如有更好的方法可以fork
extension UIDevice {
    static var isNetworkConnect: Bool {
        let network = NetworkReachabilityManager()
        return network?.isReachable ?? true // 无返回就默认网络已连接
    }
}

